import * as vscode from 'vscode';
import * as path from 'path';
import { MagicResourceType } from '../types';
import { MirrorFileMeta } from './types';

/**
 * 镜像合并管理器
 * 处理文件合并相关的逻辑
 */
export class MirrorMergeManager {
    private toPosix(rel: string): string {
        return rel.replace(/\\/g, '/');
    }

    private async ensureDir(uri: vscode.Uri): Promise<void> {
        try {
            await vscode.workspace.fs.stat(uri);
        } catch {
            await vscode.workspace.fs.createDirectory(uri);
        }
    }

    // 清理 .merge 子树中的空目录（自下而上），当整体空时删除 .merge
    public async cleanupMergeSubtree(mirrorRoot: vscode.Uri, dir: string): Promise<void> {
        try {
            const mergeRoot = vscode.Uri.joinPath(mirrorRoot, '.merge');
            const start = vscode.Uri.joinPath(mergeRoot, ...dir.split('/'));
            // 先删除 BASE/LOCAL/REMOTE 临时侧文件
            try {
                const entries = await vscode.workspace.fs.readDirectory(start);
                for (const [name, type] of entries) {
                    if (type !== vscode.FileType.File) continue;
                    if (/\.(BASE|LOCAL|REMOTE)\.ms$/.test(name) || /\.(BASE|LOCAL|REMOTE)\.meta\.json$/.test(name)) {
                        try { await vscode.workspace.fs.delete(vscode.Uri.joinPath(start, name), { useTrash: false }); } catch(e) {console.warn(e)}
                    }
                }
            } catch(e) {console.warn(e)}

            let current = start;
            while (true) {
                const rel = this.toPosix(path.relative(mergeRoot.fsPath, current.fsPath));
                if (rel.startsWith('..')) break;
                let stat: vscode.FileStat | null = null;
                try {
                    stat = await vscode.workspace.fs.stat(current);
                } catch {
                    stat = null;
                }
                if (!stat) {
                    const parent = vscode.Uri.joinPath(current, '..');
                    const parentRel = this.toPosix(path.relative(mergeRoot.fsPath, parent.fsPath));
                    if (parentRel.startsWith('..')) break;
                    current = parent;
                    continue;
                }
                if (stat.type !== vscode.FileType.Directory) {
                    const parent = vscode.Uri.joinPath(current, '..');
                    const parentRel = this.toPosix(path.relative(mergeRoot.fsPath, parent.fsPath));
                    if (parentRel.startsWith('..')) break;
                    current = parent;
                    continue;
                }
                const entries = await vscode.workspace.fs.readDirectory(current);
                if (entries.length === 0) {
                    await vscode.workspace.fs.delete(current, { recursive: false, useTrash: false });
                    const parent = vscode.Uri.joinPath(current, '..');
                    const parentRel = this.toPosix(path.relative(mergeRoot.fsPath, parent.fsPath));
                    if (parentRel.startsWith('..')) break;
                    current = parent;
                    continue;
                }
                break;
            }
        } catch {
            // 忽略清理错误，尽力而为
        }
    }

    // 打开差异/合并：使用 VS Code 三方合并窗口（local/remote/base -> result）
    public async promptMergeForFile(fileName: string, dir: string, mirrorRoot: vscode.Uri): Promise<void> {
        // 本地与服务器脚本 URI（按类型选择扩展名）
        const typeFromDir = (dir.split('/')[0] || '') as MagicResourceType;
        const ext = typeFromDir === 'datasource' ? '.json' : '.ms';
        const localUri = vscode.Uri.joinPath(mirrorRoot, ...dir.split('/'), `${fileName}${ext}`);
        const serverUri = vscode.Uri.parse(`magic-api:/${dir}/${fileName}${ext}`);

        let localScript = '';
        let serverScript = '';
        try { localScript = Buffer.from(await vscode.workspace.fs.readFile(localUri)).toString('utf8'); } catch(e) {console.warn(e)}
        try { serverScript = Buffer.from(await vscode.workspace.fs.readFile(serverUri)).toString('utf8'); } catch(e) {console.warn(e)}

        // 根据本地与远端 meta 时间生成“新/旧”标签，用于标题描述
        const segs = dir.split('/');
        const type = segs[0] as MagicResourceType;
        const groupPathSub = segs.slice(1).join('/');
        const localMeta = await this.readLocalMeta(mirrorRoot, type, groupPathSub, fileName);
        let serverTime = 0;
        try {
            const client = (await import('../serverManager')).ServerManager.getInstance().getCurrentClient();
            if (client) {
                await client.getResourceFiles(dir);
                const fid = client.getFileIdByPath(`${dir}/${fileName}${type === 'datasource' ? '.json' : '.ms'}`);
                const info = fid ? await client.getFile(fid) : null;
                serverTime = info?.updateTime ?? 0;
            }
        } catch(e) {console.warn(e)}
        const localTime = localMeta?.localUpdateTime ?? 0;

        const tempBaseDir = vscode.Uri.joinPath(mirrorRoot, '.merge', ...dir.split('/'));
        await this.ensureDir(tempBaseDir);
        // 注意：合并输出固定使用 .ms 以兼容保存监听器
        const resultUri = vscode.Uri.joinPath(tempBaseDir, `${fileName}.ms`);
        const baseTemp = vscode.Uri.joinPath(tempBaseDir, `${fileName}.BASE.ms`);
        const leftTemp = vscode.Uri.joinPath(tempBaseDir, `${fileName}.LOCAL.ms`);
        const rightTemp = vscode.Uri.joinPath(tempBaseDir, `${fileName}.REMOTE.ms`);

        try {
            await vscode.workspace.fs.writeFile(leftTemp, Buffer.from(localScript || '', 'utf8'));
            await vscode.workspace.fs.writeFile(rightTemp, Buffer.from(serverScript || '', 'utf8'));
            const baseScript = (localTime <= serverTime) ? (localScript || '') : (serverScript || '');
            await vscode.workspace.fs.writeFile(baseTemp, Buffer.from(baseScript, 'utf8'));
            // 让结果文件存在，初始以本地为准
            await vscode.workspace.fs.writeFile(resultUri, Buffer.from(localScript || '', 'utf8'));

            let opened = false;
            const argsPreferred = {
                base: { uri: baseTemp, title: '基础' },
                input1: { uri: leftTemp, title: '本地' },
                input2: { uri: rightTemp, title: '服务器' },
                result: { uri: resultUri, title: `脚本合并: ${fileName}` }
            };
            const argsFallback = {
                base: baseTemp,
                input1: { uri: leftTemp, title: '本地' },
                input2: { uri: rightTemp, title: '服务器' },
                output: resultUri
            };
            try {
                await vscode.commands.executeCommand('vscode.openMergeEditor', argsPreferred);
                opened = true;
            } catch(e) {console.warn(e)}
            if (!opened) {
                try {
                    await vscode.commands.executeCommand('_open.mergeEditor', argsFallback);
                    opened = true;
                } catch(e) {console.warn(e)}
            }
            if (opened) {
                vscode.window.showInformationMessage('合并窗口已打开');
                return;
            }
            // 若两种命令均不可用，继续进入 catch 分支处理
        } catch (err) {
            vscode.window.showErrorMessage(`打开合并窗口失败: ${String(err)}`);
            // 兜底：尝试直接以结果文件打开合并编辑器
            try { await vscode.commands.executeCommand('vscode.openWith', resultUri, 'vscode.mergeEditor'); } catch(e) {console.warn(e)}
        }
    }

    // 打开元数据差异/合并：使用 VS Code 三方合并窗口
    public async promptMergeForMeta(fileName: string, dir: string, mirrorRoot: vscode.Uri): Promise<void> {
        const segs = dir.split('/');
        const type = segs[0] as MagicResourceType;
        const groupPathSub = segs.slice(1).join('/');
        const localMeta = await this.readLocalMeta(mirrorRoot, type, groupPathSub, fileName);

        // 取服务器元数据
        let serverMeta: MirrorFileMeta | null = null;
        try {
            const client = (await import('../serverManager')).ServerManager.getInstance().getCurrentClient();
            if (client) {
                await client.getResourceFiles(dir);
                const fid = client.getFileIdByPath(`${dir}/${fileName}.ms`);
                const info = fid ? await client.getFile(fid) : null;
                serverMeta = info ? this.toMirrorMetaFromServer(info) : null;
            }
        } catch(e) {console.warn(e)}

        // 构建用于 diff 的净化副本：忽略本地字段与扩展字段
        const leftObj = this.sanitizeMetaForDiff(localMeta) || { name: fileName, type, groupPath: dir };
        const rightObj = this.sanitizeMetaForDiff(serverMeta) || { name: fileName, type, groupPath: dir };
        const localJson = JSON.stringify(leftObj, null, 2);
        const serverJson = JSON.stringify(rightObj, null, 2);
        const localTime = localMeta?.localUpdateTime ?? 0;
        const serverTime = serverMeta?.updateTime ?? 0;

        const tempBaseDir = vscode.Uri.joinPath(mirrorRoot, '.merge', ...dir.split('/'));
        await this.ensureDir(tempBaseDir);
        const resultUri = vscode.Uri.joinPath(tempBaseDir, `${fileName}.meta.json`);
        const baseTemp = vscode.Uri.joinPath(tempBaseDir, `${fileName}.BASE.meta.json`);
        const leftTemp = vscode.Uri.joinPath(tempBaseDir, `${fileName}.LOCAL.meta.json`);
        const rightTemp = vscode.Uri.joinPath(tempBaseDir, `${fileName}.REMOTE.meta.json`);

        try {
            await vscode.workspace.fs.writeFile(leftTemp, Buffer.from(localJson || '', 'utf8'));
            await vscode.workspace.fs.writeFile(rightTemp, Buffer.from(serverJson || '', 'utf8'));
            const baseJson = (localTime <= serverTime) ? (localJson || '') : (serverJson || '');
            await vscode.workspace.fs.writeFile(baseTemp, Buffer.from(baseJson, 'utf8'));
            // 让结果文件存在，初始以本地为准
            await vscode.workspace.fs.writeFile(resultUri, Buffer.from(localJson || '', 'utf8'));

            let opened = false;
            const argsPreferred = {
                base: { uri: baseTemp, title: '基础' },
                input1: { uri: leftTemp, title: '本地' },
                input2: { uri: rightTemp, title: '服务器' },
                result: { uri: resultUri, title: `元数据合并: ${fileName}` }
            };
            const argsFallback = {
                base: baseTemp,
                input1: { uri: leftTemp, title: '本地' },
                input2: { uri: rightTemp, title: '服务器' },
                output: resultUri
            };
            try {
                await vscode.commands.executeCommand('vscode.openMergeEditor', argsPreferred);
                opened = true;
            } catch(e) {console.warn(e)}
            if (!opened) {
                try {
                    await vscode.commands.executeCommand('_open.mergeEditor', argsFallback);
                    opened = true;
                } catch(e) {console.warn(e)}
            }
            if (opened) {
                vscode.window.showInformationMessage('合并窗口已打开');
                return;
            }
            // 若两种命令均不可用，继续进入 catch 分支处理
        } catch (err) {
            vscode.window.showErrorMessage(`打开合并窗口失败: ${String(err)}`);
            try { await vscode.commands.executeCommand('vscode.openWith', resultUri, 'vscode.mergeEditor'); } catch(e) {console.warn(e)}
        }
    }

    // 辅助方法：需要从外部提供或导入
    // 辅助方法需要从外部注入
    private readLocalMeta!: (mirrorRoot: vscode.Uri, type: MagicResourceType, groupPathSub: string | undefined, fileName: string) => Promise<MirrorFileMeta | null>;
    private toMirrorMetaFromServer!: (info: any) => MirrorFileMeta;
    private sanitizeMetaForDiff!: (meta: MirrorFileMeta | null) => Record<string, any> | null;

    // 设置依赖注入的方法
    public setDependencies(
        readLocalMeta: (mirrorRoot: vscode.Uri, type: MagicResourceType, groupPathSub: string | undefined, fileName: string) => Promise<MirrorFileMeta | null>,
        toMirrorMetaFromServer: (info: any) => MirrorFileMeta,
        sanitizeMetaForDiff: (meta: MirrorFileMeta | null) => Record<string, any> | null
    ): void {
        this.readLocalMeta = readLocalMeta;
        this.toMirrorMetaFromServer = toMirrorMetaFromServer;
        this.sanitizeMetaForDiff = sanitizeMetaForDiff;
    }
}
